package com.google.code.swingonfire.component.calendar;

import java.awt.Color;
import java.awt.Font;
import java.util.Calendar;

import javax.swing.table.AbstractTableModel;

public class JMonthTableModel extends AbstractTableModel
{

    public static class DayOfWeekEntry
    {
        private final JCalendarModel model;
        private final int dayOfWeek;

        public DayOfWeekEntry(JCalendarModel model, int dayOfWeek)
        {
            super();

            this.model = model;
            this.dayOfWeek = dayOfWeek;
        }

        public JCalendarModel getModel()
        {
            return model;
        }

        public String getDayOfWeekName()
        {
            return model.getDayOfWeekName(dayOfWeek);
        }

        public Font getDayOfWeekFont(Font defaultFont)
        {
            return model.getDayOfWeekFont(dayOfWeek, defaultFont);
        }

        public Color getDayOfWeekForeground(Color defaultColor)
        {
            return model.getDayOfWeekForeground(dayOfWeek, defaultColor);
        }
    }

    public static class WeekOfYearEntry
    {
        private final JCalendarModel model;
        private final Calendar calendar;

        public WeekOfYearEntry(JCalendarModel model, Calendar calendar)
        {
            super();

            this.model = model;
            this.calendar = calendar;
        }

        public JCalendarModel getModel()
        {
            return model;
        }

        public String getWeekOfYearName()
        {
            return model.getWeekOfYearName(calendar);
        }

        public Font getWeekOfYearFont(Font defaultFont)
        {
            return model.getWeekOfYearFont(calendar, defaultFont);
        }

        public Color getWeekOfYearForeground(Color defaultColor)
        {
            return model.getWeekOfYearForeground(calendar, defaultColor);
        }
    }

    public static class DayEntry
    {
        private final JCalendarModel model;
        private final Calendar calendar;
        private final boolean inScope;

        public DayEntry(JCalendarModel model, Calendar calendar, boolean inScope)
        {
            super();

            this.model = model;
            this.calendar = calendar;
            this.inScope = inScope;
        }

        public JCalendarModel getModel()
        {
            return model;
        }

        public Calendar getCalendar()
        {
            return calendar;
        }

        public String getDayName()
        {
            return model.getDayName(calendar, inScope);
        }

        public boolean isDaySelectable()
        {
            return model.isDaySelectable(calendar, inScope);
        }

        public Font getDayFont(Font defaultFont)
        {
            return model.getDayFont(calendar, inScope, defaultFont);
        }

        public Color getDayForeground(Color defaultColor)
        {
            return model.getDayForeground(calendar, inScope, defaultColor);
        }
    }

    private static final long serialVersionUID = -8017978959983892906L;

    private final boolean weekOfYearVisible;
    private final WeekOfYearEntry[] weekOfYearEntries = new WeekOfYearEntry[6];
    private final DayEntry[][] dayEntries = new DayEntry[7][6];

    public JMonthTableModel(JCalendarModel model, int month, int year, boolean weekOfYearVisible)
    {
        super();

        Calendar firstDay = getFirstDayOfCalendar(model, month, year);

        this.weekOfYearVisible = weekOfYearVisible;

        for (int y = 0; y < 6; y += 1)
        {
            if (weekOfYearVisible)
            {
                firstDay.getTime();
                Calendar current = (Calendar) firstDay.clone();
                weekOfYearEntries[y] = new WeekOfYearEntry(model, current);
            }

            for (int x = 0; x < 7; x += 1)
            {
                firstDay.getTime();
                Calendar current = (Calendar) firstDay.clone();
                dayEntries[x][y] = new DayEntry(model, current, month == current.get(Calendar.MONTH));
                firstDay.add(Calendar.DAY_OF_YEAR, 1);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getRowCount()
    {
        return 6;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getColumnCount()
    {
        return (weekOfYearVisible) ? 8 : 7;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object getValueAt(int rowIndex, int columnIndex)
    {
        if (weekOfYearVisible)
        {
            return (columnIndex == 0) ? weekOfYearEntries[rowIndex] : dayEntries[columnIndex - 1][rowIndex];
        }

        return dayEntries[columnIndex][rowIndex];
    }

    public static Calendar getFirstDayOfCalendar(JCalendarModel model, int month, int year)
    {
        Calendar current = (Calendar) model.getDisplayed().clone();

        current.set(Calendar.YEAR, year);
        current.set(Calendar.MONTH, month);
        current.set(Calendar.DAY_OF_MONTH, 1);
        current.add(Calendar.DAY_OF_YEAR, -1);

        int dayOfWeek = current.get(Calendar.DAY_OF_WEEK);
        int firstDayOfWeek = model.getFirstDayOfWeek();

        // OPTIMIZE this, remove the loop
        while (dayOfWeek != firstDayOfWeek)
        {
            current.add(Calendar.DAY_OF_YEAR, -1);
            dayOfWeek = current.get(Calendar.DAY_OF_WEEK);
        }

        return current;
    }

}
